﻿/************************************************************************
** 文  件：
**	Base64Util.h
** 功  能：
**	base64编解码工具类
** 作  者：
**	email:lixx2048@163.com
**  wechat:lixiang6153
**  QQ:941415509
** 日  期：
**	2024/03/15	14:00:00
** 说  明：
**	无
** 版  本：
**	2024/03/15	14:00:00		1.0		lixx2048@163.com
/************************************************************************/
#pragma once
#include <cctype>
#include <string>
#include <utility>

class Base64Util
{
public:
	// base64编码
	static std::string Encode(std::string source, int length)
	{
		Base64Util util;

		int len = util.encoded_size(length);
		char* decoded = new char[len];
		memset(decoded, 0, len);

		util.encode(decoded, source.data(), length);
		std::string strEnBase64 = std::string(decoded, len);
		delete []decoded;

		return strEnBase64;
	}

	// base64解码
	static std::string Decode(std::string source, int length)
	{
		Base64Util util;

		int len = util.decoded_size(length);
		char* evcoded = new char[len];
		memset(evcoded, 0, len);
		
		util.decode(evcoded, source.data(), length);
		std::string strDeBase64 = std::string(evcoded, len);
		delete []evcoded;

		return strDeBase64;
	}
private:
	char const* get_alphabet(){
		static char constexpr tab[] = {
		"ABCDEFGHIJKLMNOP"
		"QRSTUVWXYZabcdef"
		"ghijklmnopqrstuv"
		"wxyz0123456789+/"
		};
		return &tab[0];
	}

	signed char const* get_inverse()
	{
		static signed char constexpr tab[] = {
		 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //   0-15
		 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //  16-31
		 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, //  32-47
		 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, //  48-63
		 -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, //  64-79
		 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, //  80-95
		 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, //  96-111
		 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127
		 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143
		 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159
		 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175
		 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191
		 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207
		 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223
		 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239
		 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1  // 240-255
		};
		return &tab[0];
	}

	std::size_t encoded_size(std::size_t n){
		return 4 * ((n + 2) / 3);
	}

	std::size_t decoded_size(std::size_t n){
		return n / 4 * 3;
	}

	std::size_t encode(void* dest, void const* src, std::size_t len){
		char* out = static_cast<char*>(dest);
		char const* in = static_cast<char const*>(src);
		auto const tab = get_alphabet();

		for (auto n = len / 3; n--;)
		{
			*out++ = tab[(in[0] & 0xfc) >> 2];
			*out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
			*out++ = tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)];
			*out++ = tab[in[2] & 0x3f];
			in += 3;
		}

		switch (len % 3)
		{
		case 2:
			*out++ = tab[(in[0] & 0xfc) >> 2];
			*out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
			*out++ = tab[(in[1] & 0x0f) << 2];
			*out++ = '=';
			break;

		case 1:
			*out++ = tab[(in[0] & 0xfc) >> 2];
			*out++ = tab[((in[0] & 0x03) << 4)];
			*out++ = '=';
			*out++ = '=';
			break;

		case 0:
			break;
		}

		return out - static_cast<char*>(dest);
	}

	std::pair<std::size_t, std::size_t> decode(void* dest, char const* src, std::size_t len){
		char* out = static_cast<char*>(dest);
		auto in = reinterpret_cast<unsigned char const*>(src);
		unsigned char c3[3], c4[4] = { 0,0,0,0 };
		int i = 0;
		int j = 0;

		auto const inverse = get_inverse();

		while (len-- && *in != '=')
		{
			auto const v = inverse[*in];
			if (v == -1)
				break;
			++in;
			c4[i] = v;
			if (++i == 4)
			{
				c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
				c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
				c3[2] = ((c4[2] & 0x3) << 6) + c4[3];

				for (i = 0; i < 3; i++)
					*out++ = c3[i];
				i = 0;
			}
		}

		if (i)
		{
			c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
			c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
			c3[2] = ((c4[2] & 0x3) << 6) + c4[3];

			for (j = 0; j < i - 1; j++)
				*out++ = c3[j];
		}

		return { out - static_cast<char*>(dest),
			in - reinterpret_cast<unsigned char const*>(src) };
	}
};